package com.example.stringbufferlogger.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.io.IOException;
import java.io.Reader;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;

@MappedTypes(Map.class)
@MappedJdbcTypes(JdbcType.CLOB)
public class MapToClobTypeHandler extends BaseTypeHandler<Map<String, Object>> {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
        try {
            String json = objectMapper.writeValueAsString(parameter);
            Clob clob = ps.getConnection().createClob();
            clob.setString(1, json);
            ps.setClob(i, clob);
        } catch (Exception e) {
            throw new SQLException("Error converting Map to CLOB", e);
        }
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return convertClobToMap(rs.getClob(columnName));
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return convertClobToMap(rs.getClob(columnIndex));
    }

    @Override
    public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return convertClobToMap(cs.getClob(columnIndex));
    }

    private Map<String, Object> convertClobToMap(Clob clob) throws SQLException {
        if (clob == null) {
            return new HashMap<>();
        }
        try {
            Reader reader = clob.getCharacterStream();
            StringBuilder sb = new StringBuilder();
            char[] buffer = new char[4096];
            int bytesRead;
            while ((bytesRead = reader.read(buffer)) != -1) {
                sb.append(buffer, 0, bytesRead);
            }
            reader.close();
            return objectMapper.readValue(sb.toString(), HashMap.class);
        } catch (IOException e) {
            throw new SQLException("Error converting CLOB to Map", e);
        }
    }
}